home *** CD-ROM | disk | FTP | other *** search
Modula Implementation | 1994-09-22 | 20.8 KB | 599 lines |
- IMPLEMENTATION MODULE MathBase;
-
- (*****************************************************************************)
- (* In LPR-Modula wird die IEEE-Zahlendarstellung fuer REAL- und LONGREAL- *)
- (* Zahlen benutzt, womit folgende Aufteilung der durch solche Zahlen belegten*)
- (* Bytes gegeben ist: *)
- (* *)
- (* REAL: 4 Bytes *)
- (* Bitnr.: 30 22 0 *)
- (* ------------------------------------------- *)
- (* |s| 8-Bit-Exp. e | 23-Bit-Bruchteil f | *)
- (* ------------------------------------------- *)
- (* ^ *)
- (* | Vorzeichen Mantisse *)
- (* *)
- (* *)
- (* LONGREAL: 8 Bytes *)
- (* Bitnr.: 62 51 0 *)
- (* -------------------------------------------------------------- *)
- (* |s| 11-Bit-Exp. e | 52-Bit-Bruchteil f | *)
- (* -------------------------------------------------------------- *)
- (* *)
- (* In f wird der Absolutwert des gebrochenen Anteils der Mantisse gespei- *)
- (* chert, das Vorzeichen wird gesondert gefuehrt. Zur vollstaendigen Mantisse*)
- (* fehlt noch die eins vor dem Komma, die aber nicht abgespeichert wird, da *)
- (* sie immer vorhanden ist ( normalisierte Darstellung ). *)
- (* Der Exponent e ist eine Zahl im Zweierkomplement, die allerdings mit *)
- (* einem Offset ( BIAS ) versehen wird, sodass sie als vorzeichenlose Zahl *)
- (* behandelt werden kann. *)
- (* *)
- (* Der Wert der Zahlen laesst sich folgendermassen berechnen: *)
- (* *)
- (* REAL: (-1)^s * 2^(e-127) * 1.f ; 0 < e < 255 , 0.0 <= f < 1.0 *)
- (* *)
- (* LONGREAL: (-1)^s * 2^(e-1023) * 1.f ; 0 < e < 2047 , 0.0 <= f < 1.0 *)
- (* *)
- (* *)
- (* Der darstellbare Zahlenbereich ist dann: *)
- (* *)
- (* REAL: kleinste Zahl: 2^(1-127) * 1.0 = 1.17549..E-038 *)
- (* groesste Zahl: 2^(254-127) * 2.0-2^(-23) = 3.40282..E+038 *)
- (* *)
- (* LONGREAL: kleinste Zahl: 2^(1-1023) * 1.0 = 2.22507..E-308 *)
- (* groesste Zahl: 2^(2046-1023) * 2.0-2^(-52) = 1.79769..E+308 *)
- (* *)
- (* *)
- (* Die bei dieser Darstellung nicht verwendeten Extremwerte der Exponenten *)
- (* werden im Zusammenhang mit bestimmten Mantissenwerten zur Darstellung *)
- (* spezieller Werte und Zahlen benutzt: *)
- (* *)
- (* e = 255 bzw. 2047 und f = 0: Darstellung von Unendlich *)
- (* *)
- (* e = 255 bzw. 2047 und f # 0: Not a Number ( NAN ), nicht darstellbarer *)
- (* Zahlenwert, kann Exception ausloesen. *)
- (* *)
- (* e = 0 und f = 0: Darstellung der Null ( mit Vorzeichen ) *)
- (* *)
- (* e = 0 und f # 0: sogenannte denormalisierte Zahlen, hiermit*)
- (* koennen sehr kleine Zahlen unterhalb der *)
- (* kleinsten darstellbaren Zahl der normali- *)
- (* sierten Zahlen dargestellt werden. *)
- (* *)
- (* Fuer denormalisierte Zahlen gilt: *)
- (* *)
- (* REAL: Wert: (-1)^s * 2^(-126) * 0.f ; 0.0 < f < 1.0 *)
- (* *)
- (* kleinste Zahl: 2^(-126) * 2^(-23) = 1.40129..E-045 *)
- (* *)
- (* LONGREAL: Wert: (-1)^s * 2^(-1022) * 0.f ; 0.0 < f < 1.0 *)
- (* *)
- (* kleinste Zahl: 2^(-1022) * 2^(-52) = 4.94065..E-324 *)
- (* *)
- (* *)
- (* LPR-Modula verwendet keine denormalisierten Zahlen, sodass eine Zahl Null *)
- (* ist, wenn ihr Exponent Null ist ( gilt nicht beim Vergleich mit 0.0, da *)
- (* hier bitweise verglichen wird ). *)
- (*___________________________________________________________________________*)
- (* 13-Jan-90 , hk Beginn *)
- (* 26-Jan-90 , hk erste Version *)
- (* 28-Jan-90 , hk *)
- (* neu: "GetFraction", "GetLongFraction" *)
- (* "MakeReal", "MakeLongReal" in Assembler *)
- (* 08-Feb-90 , hk *)
- (* "SIGN", "SIGND", "INTSIGN" neu *)
- (* 03-Mae-90 , hk *)
- (* Konstanten im Definitionsmodul neu, "real","Real","entier","Entier"*)
- (* "round","Round" neu *)
- (*****************************************************************************)
-
- FROM SYSTEM IMPORT (* PROC *) VAL, SETREG, SHIFT, LONG, INLINE;
-
- (*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*)
-
- CONST
- exponent = BITSET{ 7..14 };
- longexp = BITSET{ 4..14 };
-
- TYPE
- (* Um auf Teile der REAL-Zahlen zugreifen zu koennen,
- * werden folgende Typen deklariert; mit ihnen koennen
- * die Zahlen alternativ als Folge von CARDINAL-Zahlen
- * angesprochen werden, wobei hier nur der Teil inter-
- * essant ist, der den Exponenten enthaelt.
- *)
-
- RealCard =
- RECORD
- CASE :BOOLEAN OF
- FALSE: real : REAL;
- |TRUE : ch,cl : CARDINAL;
- END;
- END;
-
- LongrealCard =
- RECORD
- CASE :BOOLEAN OF
- FALSE : longreal : LONGREAL;
- |TRUE : chh,chl,clh,cll : CARDINAL;
- END;
- END;
-
- VAR
- lr : LongrealCard; (* Fuer die "LONGREAL-Konstanten" *)
- MAXrlint,
- MINrlint : LONGREAL;
- MAXrint,
- MINrint : REAL;
-
- (*===========================================================================*)
-
- PROCEDURE SplitReal ((* EIN/ -- *) value : REAL;
- (* -- /AUS *) VAR exp : INTEGER ): REAL;
- (*T*)
- CONST bias = 127;
-
- VAR zahl : RealCard;
-
- BEGIN
- zahl.real := value;
-
- IF VAL( BITSET, zahl.ch ) * exponent = { } THEN
-
- (* Eine REAL-Zahl ist nur dann Null, wenn sowohl
- * Mantisse als auch Exponent Null sind; ein
- * CARDINAL-Vergleich ist wesentlich schneller.
- * Waere nur der Exponent Null, aber nicht die
- * Mantisse, handelte es sich um eine sog, 'denor-
- * malisierte Zahl', die aber vom Laufzeitsystem
- * nicht unterstuezt wird; deshalb reicht es, den
- * Exponenten zu ueberpruefen.
- *)
- exp := 0;
- RETURN( 0.0 );
- ELSE
- exp := ( VAL( CARDINAL,
- VAL( BITSET, zahl.ch ) * exponent ) DIV 128 ) - bias ;
-
- zahl.ch := VAL( CARDINAL,
- VAL( BITSET, zahl.ch ) - exponent ) + bias * 128;
-
- END; (* IF zahl.ch *)
-
- RETURN( zahl.real );
- END SplitReal;
-
- (*---------------------------------------------------------------------------*)
-
- PROCEDURE SplitLongReal ((* EIN/ -- *) value : LONGREAL;
- (* -- /AUS *) VAR exp : INTEGER ): LONGREAL;
- (*T*)
- CONST bias = 1023;
-
- VAR zahl : LongrealCard;
-
- BEGIN
- zahl.longreal := value;
-
- IF VAL( BITSET, zahl.chh ) * longexp = { } THEN
- exp := 0;
- RETURN( 0.0D );
- ELSE
- exp := ( VAL( CARDINAL,
- VAL( BITSET, zahl.chh ) * longexp ) DIV 16 ) - bias ;
-
- zahl.chh := VAL( CARDINAL,
- VAL( BITSET, zahl.chh ) - longexp ) + bias * 16;
-
- END; (* IF zahl.chh *)
-
- RETURN( zahl.longreal );
- END SplitLongReal;
-
- (*---------------------------------------------------------------------------*)
-
- PROCEDURE MakeReal ((* EIN/ -- *) value : REAL;
- (* EIN/ -- *) exp : INTEGER ): REAL;
- (*T*)
- (* VAR beides : RealCard; *)
-
- BEGIN
- (* beides.real := value;
-
- IF VAL( BITSET, beides.ch ) * exponent # { } THEN
-
- INC( exp, VAL( CARDINAL, VAL( BITSET, beides.ch) * exponent ) DIV 128 );
-
- IF exp < 1 THEN (* Unterlauf *)
- RETURN( 0.0 );
- ELSIF exp > 254 THEN (* Ueberlauf *)
- RETURN( MAX(REAL));
- END; (* IF exp *)
-
- beides.ch := VAL( CARDINAL, VAL( BITSET, beides.ch ) - exponent )
- + VAL( CARDINAL, exp ) * 128;
-
- END; (* IF beides.ch *)
-
- RETURN( beides.real );
-
- ++++++++++++++++++++++++++++++++++++++++++++++++++
-
- Achtung: Die Ueberlaufbehandlung ist genauer als bei
- der MODULA-Version, deswegen sollte diese
- Assemblerversion benutzt werden
-
- expmsk EQU $7F80
- maxreal EQU $7F7FFFFF
-
- exp EQU 12
- value EQU exp + 2
- RETURN EQU value + 4
-
- MakeReal:
- move.l value(a6), d0
- swap d0 ; Es interessiert nur der Exponent
- move.w d0, d1 ; auch im 'Arbeitsregister'
- andi.w #$FFFF-expmsk, d0 ; Exponenten loeschen
- andi.w #expmsk, d1 ; hier nur den Exponenten
- beq.s ufl ; Exponent Null => Zahl Null ( nicht denorm.)
- lsr.w #7, d1 ; Exp. als CARDINAL-Zahl
- add.w exp(a6), d1 ; gewuenschten Faktor addieren
- ; und Ergebnis als INTEGER behandeln
- bvs.s ofl ; B: das war zuviel
- cmpi.w #1, d1 ; Unterlauf ?
- bge.s tstofl ; B: nein
- ufl:
- moveq #0, d0 ; keine denormalisierten Zahlen, einfach Null
- bra.s ende ;
- tstofl:
- cmpi.w #254, d1 ; Ueberlauf ?
- ble.s ok ; B: nein
- ofl:
- move.l #maxreal, d0 ; groesste REAL-Zahl zurueckgeben
- bra.s ende ;
- ok:
- lsl.w #7, d1 ; Exp. wieder an die richtige Stelle bringen
- or.w d1, d0 ;
- swap d0 ;
- ende:
- move.l d0, RETURN(a6)
- *)
- INLINE( 202EH,000EH,4840H,3200H,0240H,807FH,0241H,7F80H,670EH );
- INLINE( 0EE49H,0D26EH,000CH,6910H,0C41H,0001H,6C04H,7000H,6014H );
- INLINE( 0C41H,00FEH,6F08H,203CH,7F7FH,0FFFFH,6006H,0EF49H,8041H );
- INLINE( 4840H,2D40H,0012H );
-
- END MakeReal;
-
- (*---------------------------------------------------------------------------*)
-
- PROCEDURE MakeLongReal ((* EIN/ -- *) value : LONGREAL;
- (* EIN/ -- *) exp : INTEGER ): LONGREAL;
- (*T*)
- (* VAR beides : LongrealCard; *)
-
- BEGIN
- (* beides.longreal := value;
-
- IF VAL( BITSET, beides.chh ) * longexp # { } THEN
-
- INC( exp, VAL( CARDINAL, VAL( BITSET, beides.chh) * longexp ) DIV 16 );
-
- IF exp < 1 THEN (* Unterlauf *)
- RETURN( 0.0D );
- ELSIF exp > 2046 THEN (* Ueberlauf *)
- RETURN( MAX(LONGREAL));
- END; (* IF exp *)
-
- beides.chh := VAL( CARDINAL, VAL( BITSET, beides.chh ) - longexp )
- + VAL( CARDINAL, exp ) * 16;
-
- END; (* IF beides.chh *)
-
- RETURN( beides.longreal );
-
- ++++++++++++++++++++++++++++++++++++++++++++++++++
-
- maxlong EQU $7FEFFFFF
- expmsk EQU $7FF0
-
- exp EQU 12
- value EQU exp + 2
- RETURN EQU value + 8
-
- MakeReal:
- move.l value(a6), d0
- swap d0
- move.w d0, d1
- andi.w #$FFFF-expmsk, d0
- andi.w #expmsk, d1
- beq.s ufl
- lsr.w #4, d1
- add.w exp(a6), d1
- bvs.s ofl
- cmpi.w #1, d1
- bge.s tstofl
- ufl:
- moveq #0, d0
- move.l d0, value+4(a6) ; nicht das zweite Langwort einer LONGREAL-
- ; Zahl vergessen
- bra.s ende
- tstofl:
- cmpi.w #2046, d1
- ble.s ok
- ofl:
- moveq #-1, d0
- move.l d0, value+4(a6)
- move.l #$maxlong, d0
- bra.s ende
- ok:
- lsl.w #4, d1
- or.w d1, d0
- swap d0
- ende:
- move.l d0, RETURN(a6)
- move.l value+4(a6), RETURN+4(a6)
- *)
- INLINE( 202EH,000EH,4840H,3200H,0240H,800FH,0241H,7FF0H,670EH );
- INLINE( 0E849H,0D26EH,000CH,6914H,0C41H,0001H,6C08H,7000H,2D40H );
- INLINE( 0012H,601AH,0C41H,07FEH,6F0EH,70FFH,2D40H,0012H,203CH );
- INLINE( 7FEFH,0FFFFH,6006H,0E949H,8041H,4840H,2D40H,0016H,2D6EH );
- INLINE( 0012H,001AH );
-
- END MakeLongReal;
-
- (*---------------------------------------------------------------------------*)
-
- PROCEDURE GetFraction ((* EIN/ -- *) value : REAL;
- (* -- /AUS *) VAR int : INTEGER ): REAL;
- (*T*)
- BEGIN
- int := TRUNC( value );
-
- (* TRUNC liefert INTEGER-Zahlen, aber FLOAT
- * liefert bei negativen Zahlen falsche
- * Werte. Da der Compiler die Anwendung von
- * FLOAT auf negative Konstanten akzeptiert,
- * liegts wohl mal wieder am Laufzeitmodul.
- *
- * Das LONG() ist fuer den Fall int = MIN(INTEGER)
- * noetig.
- *)
-
- RETURN( ABS( value ) - FLOAT( ABS( LONG( int ))));
- END GetFraction;
-
- (*---------------------------------------------------------------------------*)
-
- PROCEDURE GetLongFraction ((* EIN/ -- *) value : LONGREAL;
- (* -- /AUS *) VAR int : INTEGER ): LONGREAL;
- (*T*)
- BEGIN
- int := TRUNCD( value );
-
- RETURN( ABS( value ) - FLOATD( ABS( LONG( int ))));
- END GetLongFraction;
-
- (*---------------------------------------------------------------------------*)
-
- PROCEDURE SIGN ((* EIN/ -- *) zahl : REAL ): INTEGER;
- (*T*)
- BEGIN
- (*
- expmsk EQU $7F80
-
- zahl EQU 12
- RETURN EQU zahl + 4
-
- SIGN:
- moveq #0, d2 ; Default: Sign(x) = 0
- move.w zahl(a6), d0
- move.w d0, d1
- andi.w #expmsk, d1 ; Exponent = 0 ?
- beq.s ende ; B: ja, dann Zahl Null und auch Sign(x) = 0
- moveq #1, d2
- tst.w d0 ; zahl positiv ( Bit 15 ist Vorzeichen ) ?
- bpl.s ende ; B: ja, Sign(x) = 1
- moveq #-1, d2 ; sonst Sign(x) = -1
- ende:
- move.w d2, RETURN(a6)
- *)
- INLINE( 7400H,302EH,000CH,3200H,0241H,7F80H,6708H,7401H,4A40H );
- INLINE( 6A02H,74FFH,3D42H,0010H );
-
- END SIGN;
-
- (*---------------------------------------------------------------------------*)
-
- PROCEDURE SIGND ((* EIN/ -- *) zahl : LONGREAL ): INTEGER;
- (*T*)
- BEGIN
- (* Das gleiche wie "SIGN", nur anderer
- Offset fuer RETURN, und andere Exponentmaske
-
- expmsk EQU $7FF0
-
- zahl EQU 12
- RETURN EQU zahl + 8
-
- SIGND:
- moveq #0, d2
- move.w zahl(a6), d0
- move.w d0, d1
- andi.w #expmsk, d1
- beq.s ende
- moveq #1, d2
- tst.w d0
- bpl.s ende
- moveq #-1, d2
- ende:
- move.w d2, RETURN(a6)
- *)
- INLINE( 7400H,302EH,000CH,3200H,0241H,7FF0H,6708H,7401H,4A40H );
- INLINE( 6A02H,74FFH,3D42H,0014H );
-
- END SIGND;
-
- (*---------------------------------------------------------------------------*)
-
- PROCEDURE INTSIGN ((* EIN/ -- *) zahl : LONGINT ): INTEGER;
- (*T*)
- BEGIN
- (*
- zahl EQU 12
- RETURN EQU zahl + 4
-
- INTSIGN:
- move.l zahl(a6), d0
- beq.s ende
- bmi.s neg
- moveq #1, d0
- bra.s ende
- neg:
- moveq #-1, d0
- ende2:
- move.w d0, RETURN(a6)
- *)
- INLINE( 202EH,000CH,6708H,6B04H,7001H,6002H,70FFH,3D40H,0010H );
-
- END INTSIGN;
-
- (*---------------------------------------------------------------------------*)
-
- PROCEDURE real ((* EIN/ -- *) int : INTEGER ): REAL;
- (*T*)
- BEGIN
- IF int < 0 THEN
- RETURN( -FLOAT( -LONG( int )));
- (* LONG( int ), damit auch
- * int = MIN( INTEGER ) funktioniert.
- *)
- ELSE
- RETURN( FLOAT( int ));
- END;
- END real;
-
- (*---------------------------------------------------------------------------*)
-
- PROCEDURE Real ((* EIN/ -- *) lint : LONGINT ): LONGREAL;
- (*T*)
- BEGIN
- RETURN( FLOATD( lint )); (* FLOATD funktioniert auch bei negativen Zahlen *)
- END Real;
-
- (*---------------------------------------------------------------------------*)
-
- PROCEDURE entier ((* EIN/ -- *) real : REAL ): INTEGER;
- (*T*)
- BEGIN
- IF SIGN( real ) >= 0 THEN
- IF real >= MAXint THEN
- RETURN( MAX( INTEGER ));
- ELSE
- RETURN( TRUNC( real ));
- END;
- ELSIF real <= MINint THEN
- (* Diesen Wert packt TRUNC nicht mehr,
- * deshalb gesondert betrachten
- *)
- RETURN( MIN( INTEGER ));
- ELSIF -FLOAT( TRUNC( -real )) = real THEN
- (* Wenn <real> eine glatte negative Zahl ist,
- * dann nicht um eins verringern.
- *)
- RETURN( TRUNC( real ));
- ELSE
- RETURN( TRUNC( real ) - 1 );
- END;
- END entier;
-
- (*---------------------------------------------------------------------------*)
-
- PROCEDURE Entier ((* EIN/ -- *) lreal : LONGREAL ): LONGINT;
- (*T*)
- BEGIN
- IF SIGND( lreal ) >= 0 THEN
- IF lreal >= MAXlongint THEN
- RETURN( MAX( LONGINT ));
- ELSE
- RETURN( TRUNCD( lreal ));
- END;
- ELSIF lreal <= MINlongint THEN
- (* Diesen Wert packt TRUNCD nicht mehr,
- * deshalb gesondert betrachten
- *)
- RETURN( MIN( LONGINT ));
- ELSIF -FLOATD( TRUNCD( -lreal )) = lreal THEN
- (* Wenn <lreal> eine glatte negative Zahl ist,
- * dann nicht um eins verringern.
- *)
- RETURN( TRUNCD( lreal ));
- ELSE
- RETURN( TRUNCD( lreal ) - 1D );
- END;
- END Entier;
-
- (*---------------------------------------------------------------------------*)
-
- PROCEDURE round ((* EIN/ -- *) real : REAL ): INTEGER;
- (*T*)
- BEGIN
- IF SIGN( real ) = -1 THEN
- IF real <= MINrint THEN
- RETURN( MIN( INTEGER ));
- ELSE
- RETURN( TRUNC( real - 0.5 ));
- END;
- ELSE
- IF real >= MAXrint THEN
- RETURN( MAX( INTEGER ));
- ELSE
- RETURN( TRUNC( real + 0.5 ));
- END;
- END;
- END round;
-
- (*---------------------------------------------------------------------------*)
-
- PROCEDURE Round ((* EIN/ -- *) lreal : LONGREAL ): LONGINT;
- (*T*)
- BEGIN
- IF SIGND( lreal ) = -1 THEN
- IF lreal <= MINrlint THEN
- RETURN( MIN( LONGINT ));
- ELSE
- RETURN( TRUNCD( lreal - 0.5D ));
- END;
- ELSE
- IF lreal >= MAXrlint THEN
- RETURN( MAX( LONGINT ));
- ELSE
- RETURN( TRUNCD( lreal + 0.5D ));
- END;
- END;
- END Round;
-
- (*===========================================================================*)
-
- BEGIN (* MathBase *)
- MINlongint := -2147483648.0D;
-
- WITH lr DO
- chh := 0010H;
- chl := 0000H;
- clh := 0000H;
- cll := 0000H;
-
- MINLONGREAL := longreal;
- END;
-
- MAXrlint := MAXlongint + ( -0.5D );
- MINrlint := MINlongint + 0.5D;
-
- MAXrint := MAXint - 0.5;
- MINrint := MINint + 0.5;
-
-
- END MathBase.
-